﻿using RabbitMQ.Client;
using System.Text;

namespace Core.RabbitMQ.Producer
{
    public class RabbitMqProducer : IRabbitMqProducer, IDisposable
    {
        public RabbitMqOptions RabbitMqOptions { get; }

        private readonly IConnection _connection;
        private readonly IModel _publisherModel;

        /// <summary>
        /// RabbitMQ生产者
        /// </summary>
        /// <param name="connectionProxy"></param>
        public RabbitMqProducer(IRabbitMqConnectionProxy connectionProxy)
        {

            RabbitMqOptions = connectionProxy.RabbitMqOptions;
            _connection = connectionProxy.CreateConnection();
            _publisherModel = _connection.CreateModel();
        }


        public Task BeginTrans()
        {
            _publisherModel.TxSelect();
            return Task.CompletedTask;
        }

        public Task CommitTrans()
        {
            _publisherModel.TxCommit();
            return Task.CompletedTask;
        }

        public Task RollbackTrans()
        {
            _publisherModel.TxRollback();
            return Task.CompletedTask;
        }

        /// <summary>
        /// 异步推送消息
        /// </summary>
        /// <param name="message"></param>
        /// <param name="queueName"></param>
        /// <param name="exchangeName"></param>
        /// <param name="routingKey"></param>
        /// <param name="exchangeType"></param>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Task PublishAsync(string message, string queueName, string exchangeName = "", string routingKey = "",
            ExchangeTypeEnum exchangeType = ExchangeTypeEnum.Topic,
            IBasicProperties? properties = null)
        {
            Publish(message, queueName, exchangeName, routingKey, exchangeType, properties);
            return Task.CompletedTask;
        }

        /// <summary>
        /// 推送消息
        /// </summary>
        /// <param name="message"></param>
        /// <param name="queueName"></param>
        /// <param name="exchangeName"></param>
        /// <param name="routingKey"></param>
        /// <param name="exchangeType"></param>
        /// <param name="properties"></param>
        public void Publish(string message, string queueName, string exchangeName = "", string routingKey = "", ExchangeTypeEnum exchangeType = ExchangeTypeEnum.Topic, IBasicProperties? properties = null)
        {
            exchangeName = string.IsNullOrEmpty(exchangeName) ? $"{queueName}_Exchange" : exchangeName;
            routingKey = string.IsNullOrEmpty(routingKey) ? $"{queueName}_RoutingKey" : routingKey;

            ConnectionReady(queueName, exchangeName, routingKey, exchangeType);

            var msgArr = Encoding.UTF8.GetBytes(message);
            if (properties==null||string.IsNullOrEmpty(properties.MessageId))
            {
                properties = _publisherModel.CreateBasicProperties();
                properties.MessageId = Guid.NewGuid().ToString();
            }
            _publisherModel.BasicPublish(exchangeName, routingKey, properties, msgArr);
        }

        /// <summary>
        /// 准备连接
        /// </summary>
        /// <param name="queueName"></param>
        /// <param name="exchangeName"></param>
        /// <param name="routingKey"></param>
        /// <param name="exchangeType"></param>
        private void ConnectionReady(string queueName, string exchangeName, string routingKey, ExchangeTypeEnum exchangeType)
        {
            if (string.IsNullOrEmpty(queueName))
            {
                throw new Exception("发送消息的目标队列名称必填！");
            }

            //定义死信交换机
            _publisherModel.ExchangeDeclare(
                exchange: $"{queueName}_Dead_Exchange",
                durable: true,
                type: exchangeType.ToString().ToLower(),
                autoDelete: false
            );
            //定义死信队列
            _publisherModel.QueueDeclare(
                queue: $"{queueName}_Dead_Queue",
                durable: true,
                exclusive: false,
                autoDelete: false
            );
            //绑定死信交换机和队列
            _publisherModel.QueueBind(
                queue: $"{queueName}_Dead_Queue",
                exchange: $"{queueName}_Dead_Exchange",
                routingKey: $"{queueName}_Dead_RoutingKey"
            );

            var consumerQueueArgument = new Dictionary<string, object>
            {
                { Headers.XDeadLetterExchange, $"{queueName}_Dead_Exchange" },
                { Headers.XDeadLetterRoutingKey, $"{queueName}_Dead_RoutingKey" }
            };
            if (RabbitMqOptions.QueueTtl > 0)
            {
                consumerQueueArgument.Add(Headers.XMessageTTL, RabbitMqOptions.QueueTtl);
            }
            exchangeName = string.IsNullOrEmpty(exchangeName) ? $"{queueName}_Exchange" : exchangeName;
            routingKey = string.IsNullOrEmpty(routingKey) ? $"{queueName}_RoutingKey" : routingKey;
            //定义交换机
            _publisherModel.ExchangeDeclare(
                exchange: exchangeName,
                durable: true,
                type: exchangeType.ToString().ToLower(),
                autoDelete: false
            );
            //定义队列
            _publisherModel.QueueDeclare(
                queue: queueName,
                durable: true,
                exclusive: false,
                autoDelete: false,
                consumerQueueArgument
            );
            //绑定交换机和队列
            _publisherModel.QueueBind(
                queue: queueName,
                exchange: exchangeName,
                routingKey: routingKey
            );

        }

        private bool _isDisposed;

        /// <summary>
        /// 释放连接
        /// </summary>
        public void Dispose()
        {
            if (_isDisposed) return;
            _isDisposed = true;

            _publisherModel.Close();
            _publisherModel.Dispose();

            _connection.Close();
            _connection.Dispose();
            GC.SuppressFinalize(this);
        }

        ~RabbitMqProducer()
        {
            Dispose();
        }
    }
}
